﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqToTwitter;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

/**
 * Contains all twitter specific client functions
 * 
 * This class must quarantee that any other class does not know anything about LinqTwitter, this makes
 * mantaining dependencies easier.
 * 
 * Uses LinqToTwitter to handle Twitter-connection
 * 
 * 
 * @author Sigmar Muuga
 */
namespace twilters
{
    public static class NavigationService
    {
        // Copied from http://geekswithblogs.net/casualjim/archive/2005/12/01/61722.aspx
       private static readonly Regex RE_URL = new Regex(@"(?#Protocol)(?:(?:ht|f)tp(?:s?)\:\/\/|~/|/)?(?#Username:Password)(?:\w+:\w+@)?(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?");

        public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
            "Text",
            typeof(string),
            typeof(NavigationService),
            new PropertyMetadata(null, OnTextChanged)
        );

        public static string GetText(DependencyObject d)
        { return d.GetValue(TextProperty) as string; }

        public static void SetText(DependencyObject d, string value)
        { d.SetValue(TextProperty, value); }

        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var text_block = d as TextBlock;
            if (text_block == null)
                return;

            text_block.Inlines.Clear();

            var new_text = (string)e.NewValue;
            if (string.IsNullOrEmpty(new_text))
                return;

            // Find all URLs using a regular expression
            int last_pos = 0;
            foreach (Match match in RE_URL.Matches(new_text))
            {
                // Copy raw string from the last position up to the match
                if (match.Index != last_pos)
                {
                    var raw_text = new_text.Substring(last_pos, match.Index - last_pos);
                    text_block.Inlines.Add(new Run(raw_text));
                }

                // Create a hyperlink for the match
                
                Hyperlink link = new Hyperlink(new Run(match.Value));
                try
                {
                    link.NavigateUri = new Uri(match.Value);
                }
                catch { }
               
                link.Click += OnUrlClick;

                text_block.Inlines.Add(link);

                // Update the last matched position
                last_pos = match.Index + match.Length;
            }

            // Finally, copy the remainder of the string
            if (last_pos < new_text.Length)
                text_block.Inlines.Add(new Run(new_text.Substring(last_pos)));
        }

        private static void OnUrlClick(object sender, RoutedEventArgs e)
        {
          
            System.Diagnostics.Process.Start(((Hyperlink)sender).NavigateUri.ToString());
           
            
        }
    }
    
    public class TwitterClientImpl : TwitterClient
    {
        private TwitterContext twitterContext;
        private String baseUrl;
        private String searchUrl;
        private String currentUser;

        public TwitterClientImpl(String baseUrl, String searchUrl)
        {
            this.baseUrl = baseUrl;
            this.searchUrl = searchUrl;
        }

        #region public methods

        /**
         * Initialize Twitter client with given username and password
         * Fills twitterContext with valid context
         */
        public void loginAndInitialize(String username, String password)
        {
            UsernamePasswordAuthorization auth = new UsernamePasswordAuthorization(null);
            auth.UserName = username;
            auth.Password = password;
            auth.UserAgent = "Twilters";

            

            TwitterContext context = new TwitterContext(auth, baseUrl, searchUrl);
            
            // TODO: specify possible known errors to different catch blocks
            // handle error
            try
            {
                auth.SignOn();
                TwitterCtx = context;
                currentUser = username;
            }
            catch (Exception exc)
            {
                throw new TwitterClientException("An internal error occured: " + exc.Message);
            }
        }

        public String CurrentUserName()
        {
            return currentUser;
        }

        public void logout()
        {
            checkContextValidity();
            TwitterCtx.AuthorizedClient.SignOff();
        }

        public bool isLogged()
        {
            // TODO: find a proper way to do this
            try
            {
                // dunno why == NULL doesnt work...
                try
                {
                    return TwitterCtx.AuthorizedClient.IsAuthorized;
                }
                catch (NullReferenceException nre)
                {
                    return false;
                }
            }
            catch (Exception exc)
            {
                throw new TwitterClientException("Internal error occured: " + exc.Message);
            }
        }

        public List<Tweet> findLatestTweets(TweetType type)
        {
            checkContextValidity();
            Object statusTypeTranslated = translateTweetType(type);
            if (!(statusTypeTranslated is StatusType))
            {
                throw new TwitterClientException("Invalid status type");
            }
            StatusType statusType = (StatusType)statusTypeTranslated;

            List<Status> foundTweets = (from tweet in TwitterCtx.Status where tweet.Type == statusType select tweet).ToList();
            List<Tweet> tweets = new List<Tweet>();
            foreach (Status status in foundTweets)
            {
                Tweet tweet = new Tweet(status.User.ScreenName, status.Text, status.User.ProfileImageUrl);
                tweet.Id = status.ID;
                tweet.CreateDate = status.CreatedAt;
                tweet.SetFieldValue(TweetField.CreateDate, status.CreatedAt);
                tweet.FollowerUsername = CurrentUserName();
                tweets.Add(tweet);
            }
            tweets.Sort(delegate(Tweet t1, Tweet t2) { return -t1.CreateDate.CompareTo(t2.CreateDate); });
            return tweets;
        }

        public void postNewTweet(String tweetMessage, String inReplyToID)
        {
            checkContextValidity();
            TwitterCtx.UpdateStatus(tweetMessage, inReplyToID);
        }

        public void postNewTweet(String tweetMessage)
        {
            checkContextValidity();
            TwitterCtx.UpdateStatus(tweetMessage);
        }

        public void removeTweet(String tweetId)
        {
            checkContextValidity();
            TwitterCtx.DestroyStatus(tweetId);
        }

        public void sendDirectMessage(String userID, String text) 
        {
            checkContextValidity();
            TwitterCtx.NewDirectMessage(userID, text);
        }

        public void Close()
        {
            if (isLogged())
            {
                this.logout();
            }
        }

        #endregion

        #region private methods

        protected object translateTweetType(TweetType type)
        {
            switch (type)
            {
                case TweetType.Friends:
                    return StatusType.Friends;
                case TweetType.Mentions:
                    return StatusType.Mentions;
                case TweetType.Public:
                    return StatusType.Mentions;
                default:
                    throw new TwitterClientException("No TweetType given or TweetType undefined");
            }
        }

        private void checkContextValidity() 
        {
            try
            {
                Object o = TwitterCtx.AuthorizedClient;
                o = null;
            }
            catch (NullReferenceException nre)
            {
                throw new TwitterClientException("Twitter context not initilized");
            }
        }

        private TwitterContext TwitterCtx
        {
            get
            {
                return twitterContext;
            }
            set
            {
                twitterContext = value;
            }
        }

        #endregion
    }
}
